//`timescale 1ns/1ps
module NTT_Addr_Gen
    #(parameter latency = 1 , parameter blockA = 5, parameter blockB=8, 
      parameter RAM1_DEPTH = blockA * 128, RAM2_DEPTH = blockB * 128) //流水线延迟周期
    (
    input  wire clk,rst_n,
    input  wire start, 
    input  wire NTT_Mode,       //NTT_Mode:NTT模式选择，0：NTT正变换，1：NTT逆变换
    input  wire R_Ctr,    // 0: RAM1->RAM2  1:RAM2->RAM1
    output wire finish,
    output reg busy,
    output reg  [2:0] floor_cnt,          //当前大轮轮数
    output wire [4:0] R1_AddrA,R1_AddrB,
    output wire [4:0] R2_AddrA,R2_AddrB,
    output wire R1_ena,R1_enb,R2_ena,R2_enb,
    output wire R1_wea,R1_web,R2_wea,R2_web,
    output wire [6:0] Addr_twiddle0,Addr_twiddle1,Addr_twiddle2,Addr_twiddle3,
    output wire twiddle_reverse);

    reg  [$clog2(latency):0] latency_cnt;
    wire   latency_finish = (latency_cnt==latency);

    reg  [4:0] twiddle_cycle;  //旋转因子改变总轮数
    reg  [3:0] twiddle_cnt;    //当前旋转因子------i
    wire   twiddle_refresh = twiddle_cnt==(twiddle_cycle-1);



    reg  [4:0] inner_cycle;    //旋转因子内 总周期-
    reg  [3:0] inner_cnt;      //当前转转因子不变的周期内  改变ram读写地址-----j
    wire inner_refresh = inner_cnt==(inner_cycle-1) ;

    reg NTT_Mode_reg;
    wire INTT=NTT_Mode;
    wire INTT_reg=NTT_Mode_reg;

    //低位地址
    reg  [4:0]Addr_Atemp;
    reg  [4:0]Addr_Btemp;

    wire  floor_co = (twiddle_refresh & inner_refresh) | (~(INTT|INTT_reg) & (floor_cnt == 3'd6 || floor_cnt == 3'd5)  & inner_cnt == 4'd15) | ((INTT|INTT_reg) & (floor_cnt == 3'd0 || floor_cnt == 3'd1) & inner_cnt == 4'd15);
    assign finish = (floor_cnt == 3'd6) & floor_co & latency_finish;


    //延迟
    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n) latency_cnt<=0;
        else if( (start & ~busy) | (latency_finish & floor_co) ) latency_cnt<=0;
        else if( floor_co & ~latency_finish ) latency_cnt<=latency_cnt + 1'b1;
    end
    
    
    //busy
    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n) busy<=1'b0;
        else if(start & ~busy)  busy<=1'b1;
        else if(busy  & finish) busy<=1'b0;
    end

    //NTT Mode reg
    
    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n) NTT_Mode_reg<=1'b0;
        else if(start&~busy) NTT_Mode_reg<=NTT_Mode;
        else if(finish) NTT_Mode_reg<=1'b0;
    end

    //更新NTT参数 floor_cnt twiddle_cycle inner_cycle 
    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
        begin
            floor_cnt<=3'd0;
            twiddle_cycle<=5'd0;
            inner_cycle<=5'd0;
        end
        else if(start & ~busy)
        begin
            floor_cnt<=3'd0;
            case (INTT)
            1'b0 :  begin
                twiddle_cycle<=5'd1;
                inner_cycle<=5'd16;
            end
            1'b1 :  begin
                twiddle_cycle<=5'd16;
                inner_cycle<=5'd16; //第0轮不启动 第一轮才启动
            end
            endcase
        end
        else if(latency_finish & floor_co)  //轮数更迭
        begin
            floor_cnt<=floor_cnt+1;
            case(INTT_reg)
            1'b0: begin
                if(floor_cnt == 3'd4 || floor_cnt == 3'd5 ) inner_cycle <= 5'd16;
                else begin
                    twiddle_cycle <= twiddle_cycle<<1;
                    inner_cycle <= inner_cycle>>1; 
                end
            end
            1'b1: begin
                if(floor_cnt == 3'd0 ) inner_cycle <= 5'd16;
                else if(floor_cnt == 3'd1) inner_cycle <= 5'd1;
                else begin
                    twiddle_cycle <= twiddle_cycle>>1;
                    inner_cycle <= inner_cycle<<1;
                end 
            end
            endcase
        end
    end

    //twiddle_cnt
    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)  twiddle_cnt<=4'd0;
        else if(start & ~busy) twiddle_cnt<=4'd0;
        else if(busy & floor_co & ~latency_finish) twiddle_cnt<=twiddle_cnt;
        else if(busy & (twiddle_refresh & inner_refresh)) twiddle_cnt<=4'd0;
        else if(busy & inner_refresh & ~(INTT & (floor_cnt == 3'd0 || floor_cnt == 3'd1))) twiddle_cnt<=twiddle_cnt+1'b1;
    end 

    //inner_cnt
    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)  inner_cnt<=4'd0;
        else if(busy & floor_co & ~latency_finish) inner_cnt<=inner_cnt;
        else if(busy & inner_refresh ) inner_cnt<=4'd0;
        else if(busy ) inner_cnt<=inner_cnt+1'b1;
    end 

    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n) Addr_Atemp<=5'd0;
        else if(INTT|INTT_reg)  //NTT逆变换
        begin
            if(start & ~busy) Addr_Atemp<=5'd0;
            else if(latency_finish & floor_co) Addr_Atemp<=5'd0;
            else if(inner_refresh) Addr_Atemp<=twiddle_cnt+1'b1;
            else if(busy)   begin
                if(floor_cnt != 3'd0 && floor_cnt !=3'd1) Addr_Atemp <= Addr_Atemp + (8'd128>>floor_cnt); //非第0轮
                else Addr_Atemp<=Addr_Atemp+5'd2;
            end
        end
        else  //NTT正变换
        begin
            if(start & ~busy) Addr_Atemp<=0;
            else if(latency_finish & floor_co) Addr_Atemp<=5'd0;
            else if(inner_refresh) Addr_Atemp<=twiddle_cnt+1'b1;
            else if(busy) begin
                if(floor_cnt != 3'd6 && floor_cnt !=3'd5) Addr_Atemp <= Addr_Atemp + (7'd2<<floor_cnt);//非第六轮
                else  Addr_Atemp <= Addr_Atemp + 5'd2;//第六轮
            end
            
        end

    end

    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n) Addr_Btemp<=5'd31;
        else if(INTT | INTT_reg) //NTT逆变换
        begin
            if(start & ~busy) Addr_Btemp<=5'd1;
            else if(latency_finish & floor_co) 
                begin
                    if(floor_cnt == 3'd0 ) Addr_Btemp<=5'd1;
                    else Addr_Btemp<=(7'd32>>floor_cnt);
                end
            else if(inner_refresh) Addr_Btemp<=twiddle_cnt+1+(7'd64>>floor_cnt);
            else if(busy)   
                if(floor_cnt != 3'd0 && floor_cnt !=3'd1)  Addr_Btemp <= Addr_Btemp + (8'd128>>floor_cnt);
                else Addr_Btemp<=Addr_Btemp+5'd2;
        end
        else  //NTT正变换
        begin 
            if(start & ~busy) Addr_Btemp<=5'd1;
            else if(latency_finish & floor_co) begin
                if(floor_cnt == 3'd5 || floor_cnt == 3'd4) Addr_Btemp<=5'd1;
                else Addr_Btemp<=(2<<floor_cnt);
            end
            else if(inner_refresh) Addr_Btemp<=twiddle_cnt+1'b1+(1<<floor_cnt);
            else if(busy) begin
                if(floor_cnt != 3'd6 && floor_cnt != 3'd5)   Addr_Btemp <= Addr_Btemp + (7'd2<<floor_cnt);  //非第六轮
                else Addr_Btemp<=Addr_Btemp+5'd2;            //第六轮
            end
        end
        
    end
    //  Addr_twiddle_temp
    assign twiddle_reverse = NTT_Mode | NTT_Mode_reg;
    reg [6:0] Addr_twiddle0_temp,Addr_twiddle1_temp,Addr_twiddle2_temp,Addr_twiddle3_temp;

    always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n) {Addr_twiddle0_temp,Addr_twiddle1_temp,Addr_twiddle2_temp,Addr_twiddle3_temp}<=28'd0;
        else if(INTT | INTT_reg)  //NTT逆变换
        begin
            if(start & ~busy) begin  {Addr_twiddle0_temp,Addr_twiddle1_temp,Addr_twiddle2_temp,Addr_twiddle3_temp}<={7'd1,7'd65,7'd3,7'd67}; end  //start 初始化
            else if(latency_finish & floor_co)  //轮数更新 
            begin
                if(floor_cnt == 3'd0){Addr_twiddle0_temp,Addr_twiddle1_temp,Addr_twiddle2_temp,Addr_twiddle3_temp}<={7'd2,7'd2,7'd6 ,7'd6 }; //0 -> 1 轮
                else begin  //其他轮
                    Addr_twiddle0_temp <= 7'd2 << floor_cnt; 
                    Addr_twiddle1_temp <= 7'd2 << floor_cnt;
                    Addr_twiddle2_temp <= 7'd2 << floor_cnt;
                    Addr_twiddle3_temp <= 7'd2 << floor_cnt;
                end //其他轮
            end
            else //轮数内更新 
            begin
                case(floor_cnt)
                3'd0: begin 
                            Addr_twiddle0_temp <= Addr_twiddle0_temp + 7'd4; 
                            Addr_twiddle1_temp <= Addr_twiddle1_temp + 7'd4;
                            Addr_twiddle2_temp <= Addr_twiddle2_temp + 7'd4;
                            Addr_twiddle3_temp <= Addr_twiddle3_temp + 7'd4;
                      end 
                3'd1: begin 
                            Addr_twiddle0_temp <= Addr_twiddle0_temp + 7'd8; 
                            Addr_twiddle1_temp <= Addr_twiddle1_temp + 7'd8;
                            Addr_twiddle2_temp <= Addr_twiddle2_temp + 7'd8;
                            Addr_twiddle3_temp <= Addr_twiddle3_temp + 7'd8;
                      end 
                default :begin 
                        if(inner_refresh) 
                            begin 
                                    Addr_twiddle0_temp <= Addr_twiddle0_temp + (7'd2 << floor_cnt); 
                                    Addr_twiddle1_temp <= Addr_twiddle1_temp + (7'd2 << floor_cnt);
                                    Addr_twiddle2_temp <= Addr_twiddle2_temp + (7'd2 << floor_cnt);
                                    Addr_twiddle3_temp <= Addr_twiddle3_temp + (7'd2 << floor_cnt);
                            end 
                        end
                endcase
            end
        end
        else 
        begin //NTT正变换
            if(start & ~busy) begin {Addr_twiddle0_temp,Addr_twiddle1_temp,Addr_twiddle2_temp,Addr_twiddle3_temp}<={7'd64,7'd64,7'd64,7'd64}; end 
            else if(latency_finish & floor_co)  begin //轮数更新
                if(floor_cnt == 3'd4)       {Addr_twiddle0_temp,Addr_twiddle1_temp,Addr_twiddle2_temp,Addr_twiddle3_temp}<={7'd2,7'd2,7'd6  ,7'd6 }; //4-> 5 轮
                else if(floor_cnt == 3'd5)  {Addr_twiddle0_temp,Addr_twiddle1_temp,Addr_twiddle2_temp,Addr_twiddle3_temp}<={7'd1,7'd65,7'd3 ,7'd67}; //5-> 6 轮
                else begin  //其他轮
                    Addr_twiddle0_temp <= 7'd32 >> floor_cnt; 
                    Addr_twiddle1_temp <= 7'd32 >> floor_cnt;
                    Addr_twiddle2_temp <= 7'd32 >> floor_cnt;
                    Addr_twiddle3_temp <= 7'd32 >> floor_cnt;
                end //其他轮
            end
            else //轮数内更新 
            begin
                case(floor_cnt)
                3'd6: begin 
                            Addr_twiddle0_temp <= Addr_twiddle0_temp + 7'd4; 
                            Addr_twiddle1_temp <= Addr_twiddle1_temp + 7'd4;
                            Addr_twiddle2_temp <= Addr_twiddle2_temp + 7'd4;
                            Addr_twiddle3_temp <= Addr_twiddle3_temp + 7'd4;
                      end 
                3'd5: begin 
                            Addr_twiddle0_temp <= Addr_twiddle0_temp + 7'd8; 
                            Addr_twiddle1_temp <= Addr_twiddle1_temp + 7'd8;
                            Addr_twiddle2_temp <= Addr_twiddle2_temp + 7'd8;
                            Addr_twiddle3_temp <= Addr_twiddle3_temp + 7'd8;
                      end 
                default :begin 
                        if(inner_refresh) 
                            begin 
                                Addr_twiddle0_temp <= Addr_twiddle0_temp + (7'd64 >> (floor_cnt-1'b1));
                                Addr_twiddle1_temp <= Addr_twiddle1_temp + (7'd64 >> (floor_cnt-1'b1));
                                Addr_twiddle2_temp <= Addr_twiddle2_temp + (7'd64 >> (floor_cnt-1'b1));
                                Addr_twiddle3_temp <= Addr_twiddle3_temp + (7'd64 >> (floor_cnt-1'b1));
                            end 
                        end
                endcase
            end
        end
    end

//地址反码
    wire [4:0] AddrA,AddrB;
    assign AddrA={Addr_Atemp[0],Addr_Atemp[1],Addr_Atemp[2],Addr_Atemp[3],Addr_Atemp[4]};
    assign AddrB={Addr_Btemp[0],Addr_Btemp[1],Addr_Btemp[2],Addr_Btemp[3],Addr_Btemp[4]};

//AddrL delay chain：  AB端口delay
    genvar  i;
    reg [4:0] AddrA_Delay[0:latency-1];
    reg [4:0] AddrB_Delay[0:latency-1];
    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n) AddrA_Delay[0] <=5'd0;
        else if(start & ~busy) AddrA_Delay[0] <=5'd0;
        else if(busy)  AddrA_Delay[0]<=AddrA;
    end
   
    generate
        for(i=1;i<latency;i=i+1) 
        begin
            always@(posedge clk or negedge rst_n)
            begin
                if(!rst_n) AddrA_Delay[i]<=5'd0;
                else if(busy)  AddrA_Delay[i]<=AddrA_Delay[i-1];
            end
        end
    endgenerate

    always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n) AddrB_Delay[0]<=5'd31;
        else if((latency_finish & floor_co)) AddrB_Delay[0]<=5'd31;
        else if(busy)  AddrB_Delay[0]<=AddrB;
    end

    generate
        for(i=1;i<latency;i=i+1) 
        begin
            always@(posedge clk or negedge rst_n)
            begin 
                if(!rst_n)AddrB_Delay[i] <=5'd31;
                else if((latency_finish & floor_co)) AddrB_Delay[i]<=5'd31;
                else if(start & ~busy) AddrB_Delay[i] <=5'd31;
                else if(busy) AddrB_Delay[i]<=AddrB_Delay[i-1];
            end
        end
    endgenerate
    //R_Ctr  0:RAM1->RAM2  1: RAM2->RAM1
    assign R1_AddrA =  floor_cnt[0] ^ R_Ctr ? AddrA_Delay[latency-1] :AddrA;
    assign R1_AddrB =  floor_cnt[0] ^ R_Ctr ? AddrB_Delay[latency-1] :AddrB;
    assign R2_AddrA = ~floor_cnt[0] ^ R_Ctr ? AddrA_Delay[latency-1] :AddrA;
    assign R2_AddrB = ~floor_cnt[0] ^ R_Ctr ? AddrB_Delay[latency-1] :AddrB;
    assign Addr_twiddle0 = INTT_reg ? 8'd128 - Addr_twiddle0_temp : Addr_twiddle0_temp;
    assign Addr_twiddle1 = INTT_reg ? 8'd128 - Addr_twiddle1_temp : Addr_twiddle1_temp;
    assign Addr_twiddle2 = INTT_reg ? 8'd128 - Addr_twiddle2_temp : Addr_twiddle2_temp;
    assign Addr_twiddle3 = INTT_reg ? 8'd128 - Addr_twiddle3_temp : Addr_twiddle3_temp;

//RAM读写控制信号
    assign R1_ena = busy;
    assign R1_enb = busy;
    assign R2_ena = busy;
    assign R2_enb = busy;

    assign R1_wea =  floor_cnt[0] ^ R_Ctr;
    assign R1_web =  floor_cnt[0] ^ R_Ctr;
    assign R2_wea = ~floor_cnt[0] ^ R_Ctr;    
    assign R2_web = ~floor_cnt[0] ^ R_Ctr;    

endmodule





// module NTT_Addr_Gen_tb;
// reg clk,start,rst_n;
// wire  [6:0] AddrA_twiddle;
// reg NTT_Mode_reg,NTT_Mode;
// wire  busy,NTT_Finish;
// NTT_Addr_Gen  #(.latency(4)) the_addr_con ( .clk(clk),.start(start),.NTT_Mode(NTT_Mode),.Addr_twiddle0(),.R_Ctr(1'b1),.Addr_twiddle1(),.Addr_twiddle2(),.Addr_twiddle3(),
// 			.R1_AddrA(),.R1_AddrB(),.R2_AddrA(),.R2_AddrB(),.busy(busy),.rst_n(rst_n),.finish(NTT_Finish));



// initial 
// begin
// rst_n=1;
// start=0;

// clk=0;

// #10 rst_n=0;
// #10 NTT_Mode<=0;
// #10 rst_n=1;

// #20 start=1;
// #20 NTT_Mode<=0;
// #50 
//     start=0;
// end


// always #5 clk=~clk;
// always @(posedge clk or negedge rst_n)
// begin
//     if(!rst_n) NTT_Mode_reg<=0;
//     else if(NTT_Finish & busy) NTT_Mode_reg<=0;
//     else if(start & ~busy) NTT_Mode_reg<=NTT_Mode;
// end


// endmodule


